/**
 * Created with JetBrains WebStorm.
 * User: Administrator
 * Date: 6/21/12
 * Time: 11:18 PM
 * To change this template use File | Settings | File Templates.
 */

var KEY = { SHIFT:16, CTRL:17, ESC:27, RIGHT:39, UP:38, LEFT:37, DOWN:40, SPACE:32,
    A:65, D:68, E:69, G:71, L:76, P:80, R:82, S:83, W:87, Z:90, OPENBRACKET:219, CLOSEBRACKET:221 };
var iOS = (navigator.userAgent.indexOf("iPhone;") != -1 ||
    navigator.userAgent.indexOf("iPod;") != -1 ||
    navigator.userAgent.indexOf("iPad;") != -1);

/**
 * Game Handler.
 *
 * Singleton instance responsible for managing the main game loop and
 * maintaining a few global references such as the canvas and frame counters.
 */
var GameHandler =
{
    /**
     * The single Game.Main derived instance
     */
    game: null,

    /**
     * True if the game is in pause state, false if running
     */
    paused: false,

    /**
     * The single canvas play field element reference
     */
    canvas: null,

    /**
     * Width of the canvas play field
     */
    width: 0,

    /**
     * Height of the canvas play field
     */
    height: 0,
    /**
     * Canvas element offset for absolute mouse position calculations
     */
    offsetX: 0,
    offsetY: 0,

    /**
     * Frame counter
     */
    frameCount: 0,
    /**
     * Last frame start time in ms
     */
    frameStart: 0,

    /**
     * Debugging output
     */
    maxfps: 0,
    frametime: 0,

    /**
     * Ideal FPS constant
     */
    FPSMS: 1000/60,

    /**
     * Init function called once by a window.onload handler
     */
    init: function (minimumSize, padding){
        var canvas = document.getElementById('canvas');
        if (!minimumSize){
            canvas.width = this.width;
            canvas.height = this.height;
        }

        this.width = canvas.width;
        this.height = canvas.height;
        console.log('width is: ' + this.width);
        console.log('heigh is: ' + this.height);
        this.canvas = canvas;
        this.minimumSize = minimumSize;
        this.padding = padding;

        if (!iOS)
        {
            window.addEventListener('resize', this.resizeHandler, false);
            window.addEventListener('scroll', this.resizeHandler, false);
        }

        // calculate the initial canvas offsets by manually calling the resize handler
        this.resizeHandler();
    },
    resizeHandler: function() {

    },

    start: function(game) {
        if (game instanceof Game.Main){
            this.game = game;
            this.frameStart = Date.now();
        }
        GameHandler.game.frame.call(GameHandler.game);
    },

    /**
     * Game pause toggle method.
     */
    pause: function(){
        if (this.paused){
            this.paused = false;
            GameHandler.game.frame.call(GameHandler.game);
        }else {
            this.paused = true;
        }
    }
};

/**
 * Game root namespace.
 * @namespace Game
 */
if (typeof Game == "undefined" || !Game){
    var Game = {};
}

(function(){
    Game.Main = function(){
        var me = this;
        document.onkeydown = function(event){
            var keyCode = (event === null ? window.event.keyCode : event.keyCode);
            if (me.curScene.onKeyDownHandler(keyCode)){
                // if the key is handled, prevent any further key
                event.preventDefault();
                event.stopPropagation();
            }
        };
        document.onkeyup = function(event){
            var keyCode = (event === null ? window.event.keyCode : event.keyCode);
            if (me.curScene.onKeyUpHandler(keyCode)){
                // if the key is handled, prevent any further key
                event.preventDefault();
                event.stopPropagation();
            }
        };
    };
    Game.Main.prototype =
    {
        curScene: null,
        interval: null,

        /*
         *  Game frame execute method
         * */
        frame: function frame(){
            // we need call Render for every scene
            var frameStart = Date.now();
            // get canvas context for a render pass
            var ctx = GameHandler.canvas.getContext('2d');
            GameHandler.frameCount++;
            this.onRenderGame(ctx);
            // calculate frame total time interval and frame multiplier for smooth animation
            var frameInterval = frameStart - GameHandler.frameStart;

            // calculate each frame time
            GameHandler.frametime = Date.now()- frameStart;
            GameHandler.frameStart = frameStart;

            var frameOffset = ~~(GameHandler.FPSMS - GameHandler.frametime);
            if (!GameHandler.paused) requestAnimationFrame(GameHandler.start, frameOffset);
        },

        onRenderGame: function onRenderGame(ctx)
        {
            this.curScene.onRenderScene(ctx);
        },

        isGamePaused: function isGamePaused() {

            return false;
        },

        isGameOver: function isGameOver() {

            return false;
        }
    }
})();

/**
 * Game scene base class
 * @namespace Game
 * @class Game.Scene
 */
(function (){
    Game.Scene = function (playable) {
        this.playable = playable;
    };

    Game.Scene.prototype = {
        playable: true,

        isPlayable: function isPlayable() {
            return this.playable;
        },

        onInitScene: function onInitScene (){

        },
        onRenderScene: function onRenderScene (ctx){

        },
        onKeyDownHandler: function onKeyDownHandler(keyCode)
        {
        },

        onKeyUpHandler: function onKeyUpHandler(keyCode)
        {
        },

        isComplete: function isComplete()
        {
            return false;
        }
    };
})();

(function(){
    Game.Actor = function(p, v){
        this.position = p;
        this.vector = v;
        return this;
    };
    Game.Actor.prototype = {
        /**
         * Actor position
         * @property position
         * @type Vector
         * */
        position: null,

        /**
         *  Actor direction
         *  @property Vector
         *  @type Vector
         */
        vector: null,

        /**
         * Alive flag
         * @property alive
         * @type boolean
         */
        alive: true,

        /**
         * Actor expiration test
         * @method expired
         * @return true if expired and to be removed from actor list , false if still in play
         */
        expired: function expired() {
            return !(this.alive);
        },

        /**
         * Actor game loop update event method. Called for each actor
         * at the start of each game loop cycle
         * @method onUpdate
         */
        onUpdate: function onUpdate(){

        },

        /**
         * Actor rendering event method. Called for each actor to
         * render for each frame
         */
        onRender: function onRender() {

        }
    };
})();

/**
 * SpriteActor base class
 * An actor that can ben rendered by bitmap file. The sprite handling code deals with the increment
 * of current frame within the supplied bitmap sprite strip image, based on animation direction,
 * animation speed and the animation length before looping. call renderSprite() each frame
 *
 * NOTE: by default sprites source images are 64px wide and 64px by N frames high and scaled to the
 * appropriate final size. Any other size input source should be set in constructor
 * @namespace Game
 * class Game.SpriteActor
 */
(function(){
    Game.SpriteActor = function (p, v, s){
        Game.SpriteActor.superclass.constructor.call(this, p, v);
        if (s) this.frameSize = s;
        return this;
    };

    // We need implement later
    extend(Game.SpriteActor, Game.Actor, {
        /*Size in pixel of the width/height of an individual frame in the image */
        frameSize: 64,

        /**
         * Animation image sprite reference
         * Sprite image source are all currently 64px wide 64px by N frames high.
         */
        animImage: null,

        /**
         * Length in frames of the sprite animation
         */
        animLength: 0,

        /**
         * Animation Direction, true for forward, false for reverse
         */
        animForward: true,

        /**
         * Animation frame inc/dec speed
         */
        animSpeed: 0,

        /**
         * Current animation frame index
         */
        animFrame: 0
    });
})();

/**
 * Image Preloader class. Executes the supplied callback function once all
 * registered images are loaded by the browser
 * @namespace Game
 * class Game.Preloader
 */
(function(){

    Game.Preloader = function(){
        this.images = new Array();
        return this;
    };

    Game.Preloader.prototype =
    {
        /**
         *  Image List
         *
         *  @property images
         *  @type Array
         */
        images: null,

        /**
         * Callback function
         * @property callback
         * @type Function
         */
        callback: null,

        /**
         * Images loader so far counter
         */
        counter: 0,

        /**
         * Add an image to the list of image to wait for
         */
        addImage: function addImage(img, url){
            var me = this;
            img.url = url;

            img.onload = function()
            {
                me.counter++;
                if (me.counter == me.images.length){
                    me.callback(me);
                }
            };
            this.images.push(img);
        },

        /**
         * Load the images and call the supplied function when ready
         */
        onLoadCallback: function onLoadCallback(fn)
        {
            this.counter = 0;
            this.callback = fn;
            for (var i = 0, j = this.images.length; i < j; i++){
                this.images[i].src = this.images[i].url;
            }
        }
    };

})();

// requestAnimFrame shim
window.requestAnimationFrame = (function(){
    return window.requestAnimationFrame         ||
           window.webkitRequestAnimationFrame   ||
           window.mozRequestAnimationFrame      ||
           window.oRequestAnimationFrame        ||
           window.msRequestAnimationFrame       ||
           function(callback, frameOffset)
           {
               window.setTimeout(callback, frameOffset);
           };
})();

